大名鼎鼎 house of orange来自于Hitcon CTF 2016中的一道 同名题目,其可以在没有free的情况下得到一块unsorted bin,因为unsorted bin的fd,bk为main_arean+88,所以可以通过申请切割unsorted bin来泄漏libc地址(没有深入了解的我一直以为这是house of orange仅此而已的用处……汗……)
House_of_orange – 2016Hitcon CTF
题目逻辑很简单,一共可以build 4次,每次build分进行3次堆分配,两次malloc,一次calloc,其中一次malloc是固定的0x10字节作为控制堆块,放着name和color的信息,另外按输入分配name 的大小。
程序漏洞
堆溢出:在upgrede 中没有规范size大小导致堆溢出
1 | int __fastcall upgrade(__int64 a1, __int64 a2) |
漏洞利用
1、漏洞libc地址
因为程序存在堆溢出,所以可以修改top_chunk的大小,在malloc源码里面对于申请的堆块大小超过了top_chunk大小,将调用sysmalloc
来进行分配,sysmalloc
针对这种情况有两种处理,一种是直接mmap出来一块内存,一种是扩展top_chunk
1 | /* |
也就是如果申请的大小>=mp_.mmap_threshold,就会mmap,所以只要我们申请的不要太大,就可以避免触发这个,mmap_threshold
的值为128*1024
,不过下面有两个assert需要检查 :
1 | old_top = av->top; |
第一个asser就是要求修改后的top_chunk_size
必须满足:
1 | 1、top_chunk_size>MINSIZE(MINISIZE)(并不知道具体值是多少,反正不要太小就行了) |
满足以上四个条件之后,继续往下执行最后会把原先的old_top给释放掉了
1 | top (av) = chunk_at_offset (heap, sizeof (*heap)); |
这样,free出来就能得到一个unsorted bin,然后再次分配就可以从unsorted bin中切割出来。
接下来就可以堆溢出构造unsorted bin attack了,再伪造vtable进行FSOP攻击
首先来看一下漏洞的触发
而_IO_flush_all_lockp
不需要攻击者手动调用,在一些情况下这个函数会被系统调用:
1、当libc执行abort流程时
2、当执行exit函数时
3、当执行流从main 函数返回时
FSOP也就 File Stream Oriented Programming
,也是一种支持程序流程的方法,只不过方式是通过攻击File Stream
来实现
先了解malloc对错误信息的处理过程,调用malloc_printerr,而malloc_printerr中用来打印错误的函数是__libc_message
,
之后又调用了abort函数,而abort函数中调用了_IO_flush_all_lockp
,这里面用到了IO_FILE_ALL
的结构,采用上虚表的方式调用,所以如果我们能修改IO_FILE
的内容那么就可以一定程度上支持流程;
那么怎么支持呢,这里又需要用到unsorted bin attack
的知识:在malloc的过程中,unsorted bin
会从链表上卸下来,就是会把bk+0x10
的位置写入本unsorted bin
的地址,也就是amin_arena的地址
我们通过硬件断点来观察一下:
可以看到,断点被触发后_IO_list_all
被修改成了指向top_chunk的地址main_arena+88
但是问题又来了,我们无法控制 main_arena的内容,那么该怎么处理呢?
这里还牵到io_file
的使用了,IO_FILE
结构中有一个字段是chain字段 ,它位于0x60偏移处,它指向的是下一个IO_FILE
结构体,如果能够控制 这个字段,就能再指定IO_FILE
的位置,它相当于是一个链表的结构;如此,,又联系到了small_chunk的问题了,在拆卸unsorted_bin的时候对属于small_bin的chunk进行了记录操作
而IO_FILE_ALL
指向的偏移0x60的位置正好是small_bin
的index为5的地方,所以upgrede的时候需要把unsortbin设置为0x60大小。
因为第一个分配在main_arena
的IO_FILE_plus
结构 的fp->mode
等值不符合要求,所以会通过chains
跳转到下一个IO_FILE_plus
,也就是我们之前设的unsorted bin,然后这个伪造的IO_FILE_plus
需要满足以下条件
1 | 1、fp->mode > 0 |
整个程序支持流程如下:
malloc --> _int_malloc --> __libc_message --> abort --> _IO_flush_all_lockp --> _IO_overflow
exp:
1 | #coding:utf-8 |
bookwriter – pwnable.tw
一样是用到house_of_orange
程序同样没有free功能
1 | ---------------------- |
Information
功能可以泄漏堆地址,可用也可不用,因为’/bin/sh\x00’也可以直接用libc地址
漏洞点
0x01
首先,自己实现的read函数没有0截断
1 | __int64 __fastcall my_read(__int64 a1, unsigned int a2) |
0x02
edit函数中每次都会更新size,通过strlen,结合my_read
没有0截断的情况,这里存在溢出可以修改到下一个chunk的size
1 | int edit() |
0x03
add函数中,两个全局变量,0x6020A0
存在堆指针,相邻的0x6020E0
存在size,且0x6020a0->0x6020e0 is 0x40 bytes (0x8 words)
也就是可以存放8个堆指针,但是add函数中到堆块的约束条件是i>8 && !qword_6020A0[i]
,也就是可以申请第9个,只要这个时候qword_6020A0[8] == 0
也就是0x6020E0 == 0
,这样,0号堆块的size位就被放上了一个堆地址,这样就造成了堆溢出
漏洞利用
1、通过edit中的溢出修改top_chunk的size,house_of_orange得到一块main_arena
进行泄漏地址
2、利用0号块的size溢出构造FSOP攻击
exp:
1 | from pwn import * |
house_of_orange攻击有一定概率失败,主要原因是在Bypass时,
1
2 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
&& _IO_OVERFLOW (fp, EOF) == EOF)
由于第一次将
_IO_list_all
支持到main_arena时,main_arena不可控,该内存随机,所以有时(fp->_mode<=0 && fp->_IO_write_ptr > fp-> _IO_write_base)结果 为0,造成执行_IO_overflow(fp,EOF) == EOF调用未知vtable错误地址,程序 abort,所以程序有一定的失败率
参考资料:
https://bbs.pediy.com/thread-222718.htm
https://tac1t0rnx.space/2018/01/10/house-of-orange/
https://wiki.x10sec.org/pwn/heap/house_of_orange/
http://p4nda.top/2017/12/15/pwnable-tw-bookwriter/